// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include <string_view>

#define ZEN_LOG_LEVEL_TRACE	   0
#define ZEN_LOG_LEVEL_DEBUG	   1
#define ZEN_LOG_LEVEL_INFO	   2
#define ZEN_LOG_LEVEL_WARN	   3
#define ZEN_LOG_LEVEL_ERROR	   4
#define ZEN_LOG_LEVEL_CRITICAL 5
#define ZEN_LOG_LEVEL_OFF	   6

#define ZEN_LEVEL_NAME_TRACE	std::string_view("trace", 5)
#define ZEN_LEVEL_NAME_DEBUG	std::string_view("debug", 5)
#define ZEN_LEVEL_NAME_INFO		std::string_view("info", 4)
#define ZEN_LEVEL_NAME_WARNING	std::string_view("warning", 7)
#define ZEN_LEVEL_NAME_ERROR	std::string_view("error", 5)
#define ZEN_LEVEL_NAME_CRITICAL std::string_view("critical", 8)
#define ZEN_LEVEL_NAME_OFF		std::string_view("off", 3)

namespace zen::logging::level {

enum LogLevel : int
{
	Trace	 = ZEN_LOG_LEVEL_TRACE,
	Debug	 = ZEN_LOG_LEVEL_DEBUG,
	Info	 = ZEN_LOG_LEVEL_INFO,
	Warn	 = ZEN_LOG_LEVEL_WARN,
	Err		 = ZEN_LOG_LEVEL_ERROR,
	Critical = ZEN_LOG_LEVEL_CRITICAL,
	Off		 = ZEN_LOG_LEVEL_OFF,
	LogLevelCount
};

LogLevel		 ParseLogLevelString(std::string_view String);
std::string_view ToStringView(LogLevel Level);

}  // namespace zen::logging::level

namespace zen::logging {

void			SetLogLevel(level::LogLevel NewLogLevel);
level::LogLevel GetLogLevel();

}  // namespace zen::logging

namespace spdlog {
class logger;
}

namespace zen::logging {

struct SourceLocation
{
	constexpr SourceLocation() = default;
	constexpr SourceLocation(const char* filename_in, int line_in, const char* funcname_in)
	: filename(filename_in)
	, line(line_in)
	, funcname(funcname_in)
	{
	}

	constexpr bool empty() const noexcept { return line == 0; }

	// IMPORTANT NOTE: the layout of this class must match the spdlog::source_loc class
	// since we currently pass a pointer to it into spdlog after casting it to
	// spdlog::source_loc*
	//
	// This is intended to be an intermediate state, before we (probably) transition off
	// spdlog entirely

	const char* filename{nullptr};
	int			line{0};
	const char* funcname{nullptr};
};

}  // namespace zen::logging

namespace zen {

struct LoggerRef
{
	LoggerRef() = default;
	LoggerRef(spdlog::logger& InLogger) : SpdLogger(&InLogger) {}

	LoggerRef Logger() { return *this; }

	bool   ShouldLog(int Level) const;
	inline operator bool() const { return SpdLogger != nullptr; }

	void					 SetLogLevel(logging::level::LogLevel NewLogLevel);
	logging::level::LogLevel GetLogLevel();

	spdlog::logger* SpdLogger = nullptr;
};

}  // namespace zen
